﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.Util;
using System.Diagnostics;


namespace cameracapture
{
    public partial class cameracapture : Form
    {
        private Capture image;        //takes images from camera as image frames
        private bool captureInProgress ; // checks if capture is

        public cameracapture()
        {
            InitializeComponent();
        }

        private void ProcessFrame(object sender, EventArgs arg)
        {
            
            Image<Bgr, Byte> ImageFrame =  image.QueryFrame();  //line 1
            Image<Bgr, Byte> img = ImageFrame.Resize(400,400, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR,true);
            Image<Gray, Byte> gray = img.Convert<Gray, Byte>().PyrDown().PyrUp();
        
            #region circle detection video
            Stopwatch watch = Stopwatch.StartNew();
            Bgr cannyThreshold = new Bgr(200, 200, 200);
            Gray cannyThresholdgray = new Gray(180);
            Gray cannyThresholdLinking = new Gray(120);
            Bgr circleAccumulatorThreshold = new Bgr(200, 200, 200);
            Gray circleAccumulatorThresholdgray = new Gray(500);
            CircleF[] circles = img.HoughCircles(
                cannyThreshold,
                circleAccumulatorThreshold,
               3.0, //Resolution of the accumulator used to detect centers of the circles
                20.0, //min distance 
                5, //min radius
                70 //max radius
                )[0]; //Get the circles from the first channel

            watch.Stop();

            #endregion

            #region Canny and edge detection
            watch.Reset(); watch.Start();

              Image<Gray, Byte> cannyEdges = gray.Canny(cannyThresholdgray, cannyThresholdLinking);
            LineSegment2D[] lines = cannyEdges.HoughLinesBinary(
                1, //Distance resolution in pixel-related units
                Math.PI / 45.0, //Angle resolution measured in radians.
                20, //threshold
                30, //min Line width
                10 //gap between lines
                )[0]; //Get the lines from the first channel
            watch.Stop();
          
            #endregion

            #region Find  rectangles video
            watch.Reset(); watch.Start();
          
            List<MCvBox2D> boxList = new List<MCvBox2D>(); //a box is a rotated rectangle
            using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
                for (
                   Contour<Point> contours = cannyEdges.FindContours(
                      Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
                      Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST,
                      storage);
                   contours != null;
                   contours = contours.HNext)
                {
                    Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.157, storage);

                    if (currentContour.Area > 250) //only consider contours with area greater than 250
                    {
                      
                         if (currentContour.Total ==4) //The contour has 4 vertices.
                        {
                            #region determine if all the angles in the contour are within [80, 100] degree
                            bool isRectangle = true;
                            Point[] pts = currentContour.ToArray();
                            LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

                            for (int i = 0; i < edges.Length; i++)
                            {
                                double angle = Math.Abs(
                                   edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
                                if (angle < 80 || angle > 100)
                                {
                                    isRectangle = false;
                                    break;
                                }
                            }
                            #endregion

                            if (isRectangle) boxList.Add(currentContour.GetMinAreaRect());
                        }
                    }
                }
            watch.Stop();
          //  msgBuilder.Append(String.Format("Triangles & Rectangles - {0} ms; ", watch.ElapsedMilliseconds));
            #endregion

            #region draw shapes video
            Image<Bgr, Byte> shapeimage = img.CopyBlank();
            
            foreach (MCvBox2D box in boxList)
            {
                img.Draw(box, new Bgr(Color.DarkOrange), 2);
            }                   

            foreach (CircleF circle in circles)
            {
                img.Draw(circle, new Bgr(Color.Blue), 2);
            }

            #endregion


            CamImageBox.Image = img;        //line 2
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
  
            #region if capture is not created, create it now
            if (image == null)
            {
                try
                {

                    image = new Capture();

                }
                catch (NullReferenceException excpt)
                {
                    MessageBox.Show(excpt.Message);
                }
            }
            #endregion

            if (image != null)
            {
                if (captureInProgress)
                {  //if camera is getting frames then stop the capture and set button Text // "Start" for resuming capture
                    btnStart.Text = "Start!"; //
                    Application.Idle -= ProcessFrame;
                }
                else
                {
                    //if camera is NOT getting frames then start the capture and set button // Text to "Stop" for pausing capture
                    btnStart.Text = "Stop";
                    Application.Idle += ProcessFrame;
                }

                captureInProgress = !captureInProgress;
            }
        }


        private void ReleaseData()
        {
            if (image != null)
                image.Dispose();
        }

        private void cameracapture_Load(object sender, EventArgs e)
        {

        }

        private void pictureButton_Click(object sender, EventArgs e)
        {
            Image<Bgr, Byte> Still = image.QueryFrame();
        PictureImageBox.Image = Still;
        Image<Gray, Byte> graystill = Still.Convert<Gray, Byte>().PyrDown().PyrUp();
        
        #region circle detection Still
        Stopwatch watch = Stopwatch.StartNew();
        Bgr cannyThreshold = new Bgr(200, 200, 200);//TODO: change this to work in any lighting w/auto-adjust camera
        Gray cannyThresholdgray = new Gray(180);
            Gray cannyThresholdLinking = new Gray(120);
        Bgr circleAccumulatorThreshold = new Bgr(200, 200, 200);
        CircleF[] circles = Still.HoughCircles(
            cannyThreshold,
            circleAccumulatorThreshold,
           3.0, //Resolution of the accumulator used to detect centers of the circles
            20.0, //min distance 
            5, //min radius
            70 //max radius
            )[0]; //Get the circles from the first channel

        watch.Stop();

        #endregion

        #region Canny and edge detection
        watch.Reset(); watch.Start();

        Image<Gray, Byte> cannyEdges = graystill.Canny(cannyThresholdgray, cannyThresholdLinking);
        LineSegment2D[] lines = cannyEdges.HoughLinesBinary(
            1, //Distance resolution in pixel-related units
            Math.PI / 45.0, //Angle resolution measured in radians.
            20, //threshold
            30, //min Line width
            10 //gap between lines
            )[0]; //Get the lines from the first channel
        watch.Stop();

        #endregion

        #region Find  rectangles video
        watch.Reset(); watch.Start();

        List<MCvBox2D> boxList = new List<MCvBox2D>(); //a box is a rotated rectangle
        using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
            for (
               Contour<Point> contours = cannyEdges.FindContours(
                  Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
                  Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST,
                  storage);
               contours != null;
               contours = contours.HNext)
            {
                Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.157, storage);

                if (currentContour.Area > 250) //only consider contours with area greater than 250
                {

                    if (currentContour.Total == 4) //The contour has 4 vertices.
                    {
                        #region determine if all the angles in the contour are within [80, 100] degree
                        bool isRectangle = true;
                        Point[] pts = currentContour.ToArray();
                        LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

                        for (int i = 0; i < edges.Length; i++)
                        {
                            double angle = Math.Abs(
                               edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
                            if (angle < 80 || angle > 100)
                            {
                                isRectangle = false;
                                break;
                            }
                        }
                        #endregion

                        if (isRectangle) boxList.Add(currentContour.GetMinAreaRect());
                    }
                }
            }
        watch.Stop();
        //  msgBuilder.Append(String.Format("Triangles & Rectangles - {0} ms; ", watch.ElapsedMilliseconds));
        #endregion

        #region draw shapes Still
        Image<Bgr, Byte> circleImage = Still.CopyBlank();
        foreach (MCvBox2D box in boxList)
        {
            Still.Draw(box, new Bgr(Color.DarkRed), 2);
        }   

        foreach (CircleF circle in circles)
        {
            PointF Midpoint = circle.Center;
            int x = (int)Midpoint.X;
            int y = (int)Midpoint.Y;
            Bgr imgcolor = Still[x,y];
            if ( imgcolor.Green < 70 )
            {
                Still.Draw(circle, new Bgr(Color.Red), 2);
            }
        }

        #endregion


            
        }

  

    }
}
